﻿//Maximum 150.000 followers
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TFMAPI
{
    /// <summary>
    /// This class is a wrapper around the twitter API for easing access to its REST methods
    /// </summary>
    public class TwitterAPIWrapper
    {

#if DEBUG
        private static readonly string GET_RATE_LIMIT_STATUS_URL = @"file://" + AppDomain.CurrentDomain.BaseDirectory + @"..\..\..\JsonSamples\rate_limit_status.json";
        private static readonly string GET_USERINFO_SCREENNAME_URL = @"file://" + AppDomain.CurrentDomain.BaseDirectory + @"..\..\..\JsonSamples\AccountInfoSample.json";
        private static readonly string GET_USERINFO_USERID_URL = @"file://" + AppDomain.CurrentDomain.BaseDirectory + @"..\..\..\JsonSamples\AccountInfoSample.json";
        private static readonly string GET_FOLLOWERS_URL = @"file://" + AppDomain.CurrentDomain.BaseDirectory + @"..\..\..\JsonSamples\FollowersInfoSample.json";
#else
        private const string GET_RATE_LIMIT_STATUS_URL = "http://api.twitter.com/1/account/rate_limit_status.json";
        private const string GET_USERINFO_SCREENNAME_URL = "http://api.twitter.com/1/users/show.json?screen_name={0}";
        private const string GET_USERINFO_USERID_URL = "http://api.twitter.com/1/users/show.json?user_id={0}";
        private const string GET_FOLLOWERS_URL = "http://api.twitter.com/1/followers/ids.json?screen_name={0}&cursor={1}";
#endif

#region Private Methods

        /// <summary>
        /// This helper method gets info about a Twitter user using the screenName method or the UserID method depending on the passed parameters.
        /// Is called by the other two public methods and used accordingly.
        /// </summary>
        /// <param name="userScreenName">The @screenname of the user to get information for. 
        /// This parameter has the biggest precendence in case of both being specified. 
        /// If you want to search by ID then pass an empty string in this parameter.</param>
        /// <param name="userId">The internal Twitter id for the user. If not needed specify 0 as the value.</param>
        /// <returns>The information about the user whose information we want to retrieve.</returns>
        /// <exception cref="InexistentTwitteruserException">Throws an exception of this class in case the user does not exist o</exception>
        /// <exception cref="QuotaExceededException">Thrown if the Twitter API quota is exceeded</exception>
        /// <exception cref="Exception">Any  other exception is re-thrown inside this method</exception>
        private static TwitterUserInfo GetTwitterUserInfo(string userScreenName, long userId)
        {
            if (String.IsNullOrWhiteSpace(userScreenName.Trim()) && userId == 0)
                throw new ArgumentOutOfRangeException("userScreenName", "The user Screen name must not be empty or you should specify a valid userId!");

            //Check if we can make a new call to the Twitter API
            CheckTwitterAPIQuota();

            try
            {
                //Get the response from the Twitter API
                string sUserInfo = "";
                if ( String.IsNullOrWhiteSpace(userScreenName.Trim()) )
                    sUserInfo = Helper.GetUrlResponse(String.Format(GET_USERINFO_USERID_URL, userId));  //By userId
                else
                    sUserInfo = Helper.GetUrlResponse(String.Format(GET_USERINFO_SCREENNAME_URL, userScreenName.Trim())); //By screenName
                
                //Get an object from  the returned JSON for easy of manipulation
                TwitterUserInfo tui = sUserInfo.json2Object<TwitterUserInfo>();

                //At end return the full information retrieved and available in the ids property
                return tui;
            }
            catch (System.Net.WebException wEx)
            {
                //{"The remote server returned an error: (404) Not Found."}
                //This means that the specified user does not exist
                if (wEx.Message.IndexOf("(404)") >= 0)
                    throw new InexistentTwitterUserException( (userId == 0) ? userScreenName : userId.ToString() );  //Notify the name or id of the user as the message for the exception
                
                //{"The remote server returned an error: (403) Forbidden"}
                //This happens if Twitter account is deactivated or needs a user+password to access its information
                if (wEx.Message.IndexOf("(403)") >= 0)
                {
                    throw new ProtectedUserInformationException ((userId == 0) ? userScreenName : userId.ToString());  //Notify the name or id of the user as the message for the exception
                }
                else
                    throw wEx;
            }
        }

        /// <summary>
        /// This method returns an instance of the type FollowerInfo with the ids of all of a user's followers in the "ids" property
        /// </summary>
        /// <param name="userScreenName">The screen name (aka @screenName) of an user</param>
        /// <param name="currentCursor">The current page to retrieve from Twitter. Given by Twitter API in a previous call. 
        /// In the first call must be -1.</param>
        /// <param name="currentFI">A reference to a FollowerInformation object for adding the next retrieved ids from twitter. 
        /// In the first call is null.</param>
        /// <returns></returns>
        /// <remarks>This is a recursive method that calls itself for retrieving all the needed pages from the Twitter API</remarks>
        /// <exception cref="QuotaExceededException">Thrown if the Twitter API quota is exceeded</exception>
        /// <exception cref="InexistentTwitterUserException">The specified user does not exis in Twitter</exception>
        /// <exception cref="Exception">Any  other exception is re-thrown inside this method</exception>
        private static FollowersInfo GetUserFollowersInfo(string userScreenName, long currentCursor, FollowersInfo currentFI)
        {

            //Check if we can make a new call to the Twitter API
            CheckTwitterAPIQuota();

            try
            {
                //Get the response from the Twitter API
                string sFollowersInfo = Helper.GetUrlResponse(String.Format(GET_FOLLOWERS_URL, userScreenName.Trim(), currentCursor));
                //Get an object from  the returned JSON for easy of manipulation
                FollowersInfo fi = sFollowersInfo.json2Object<FollowersInfo>();
                
                //Check if this is the first time the method is called
                if (currentFI == null)
                    currentFI = fi;
                else
                    currentFI.ids.AddRange(fi.ids); //Add the next page of IDs to the current FollowerInfo object

                //Check if there are information pages about followers left for retrieving
                if (fi.next_cursor > 0)
                    GetUserFollowersInfo(userScreenName, fi.next_cursor, currentFI);    //Get next page of info

                //At end return the full information retrieved and available in the ids property
                return currentFI;
            }
            catch (System.Net.WebException wEx)
            {
                //{"The remote server returned an error: (404) Not Found."}
                //This means that the specified user does not exist
                if ( wEx.Message.IndexOf("(404)") >= 0 )
                    throw new InexistentTwitterUserException(userScreenName);
                else
                    throw wEx;
            }
        }

#endregion


#region Public Methods

        /// <summary>
        /// Gets information about the current Quota of Twitter API calls for the current IP
        /// By default is limited to 150 calls/h form a single IP.
        /// Other programs can be using the Twitter API from the same IP running out of quota until an hour passes
        /// </summary>
        /// <returns></returns>
        /// <remarks>The call to this status funciotn does not consume quota.</remarks>
        public static rate_limit_status GetTwitterAPIQuota()
        {
            //Get the response from the Twitter API
            string sRate_limit_status = Helper.GetUrlResponse(GET_RATE_LIMIT_STATUS_URL);
            //Get an object from the returned JSON for easy of manipulation
            rate_limit_status rls = sRate_limit_status.json2Object<rate_limit_status>();
            //At end return the full information retrieved and available in the ids property
            return rls;
        }

        /// <summary>
        /// THis funciton checks the current Twitter API quota and raises a QuotaExceededException exception if the limit is reached 
        /// and no further API calls can be made.
        /// </summary>
        /// <exception cref="QuotaExceededException">Reports the next available time and the maximum calls per hour</exception>
        public static void CheckTwitterAPIQuota()
        {
            rate_limit_status rls = GetTwitterAPIQuota();
            if (rls.remaining_hits == 0)
                throw new QuotaExceededException(rls.resetTime, rls.hourly_limit);
        }

        /// <summary>
        /// Returns information about the specified Twitter user by her screen name
        /// </summary>
        /// <param name="userScreenName">The screen name (aka @screenName) of an user</param>
        /// <returns></returns>
        /// <exception cref="QuotaExceededException">Thrown if the Twitter API quota is exceeded</exception>
        /// <exception cref="InexistentTwitterUserException">The specified user does not exis in Twitter</exception>
        /// <exception cref="Exception">Any  other exception is re-thrown inside this method</exception>
        public static TwitterUserInfo GetTwitterUserInfo(string userScreenName)
        {
            return GetTwitterUserInfo(userScreenName, 0);
        }


        /// <summary>
        /// Returns information about the specified Twitter user by her internal Twitter userId. 
        /// Overloads the method.
        /// </summary>
        /// <param name="userId">The internal Twitter userId</param>
        /// <returns></returns>
        /// <exception cref="QuotaExceededException">Thrown if the Twitter API quota is exceeded</exception>
        /// <exception cref="InexistentTwitterUserException">The specified user does not exis in Twitter</exception>
        /// <exception cref="Exception">Any  other exception is re-thrown inside this method</exception>
        public static TwitterUserInfo GetTwitterUserInfo(long userId)
        {
            return GetTwitterUserInfo("", userId);
        }

        /// <summary>
        /// This method returns an instance of the type FollowerInfo with the ids of all of a user's followers in the "ids" property.
        /// </summary>
        /// <param name="userScreenName">The screen name (aka @screenName) of an user</param>
        /// <returns></returns>
        /// <exception cref="QuotaExceededException"></exception>
        public static FollowersInfo GetUserFollowersInfo(string userScreenName)
        {
            if (String.IsNullOrWhiteSpace(userScreenName))
                throw new ArgumentOutOfRangeException("userScreenName", "The user Screen name must not be empty!");

            return GetUserFollowersInfo(userScreenName, -1, null);
        }

#endregion
    }
}
